home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / Palettes / Chart / Source / PieChart.m < prev    next >
Text File  |  1995-06-12  |  19KB  |  744 lines

  1. // -------------------------------------------------------------------------------------
  2. // Pie Chart view
  3. // Martin D. Flynn, NeXT Computer, Inc.
  4. // -------------------------------------------------------------------------------------
  5.  
  6. #import <appkit/appkit.h>
  7. #import <libc.h>
  8. #import <c.h>
  9. #import <stdlib.h>
  10. #import <string.h>
  11. #import <math.h>
  12. #import <dpsclient/wraps.h>
  13. #import <objc/List.h>
  14. #import <objc/Storage.h>
  15. #import "PieChartPS.h"
  16. #import "PieChart.h"
  17.  
  18. // -------------------------------------------------------------------------------------
  19. // archive version number
  20. #define    VERSION            1
  21.  
  22. // -------------------------------------------------------------------------------------
  23. #define    X                origin.x
  24. #define    Y                origin.y
  25. #define    W                size.width
  26. #define    H                size.height
  27. #define    pieSLICE(I)        ((pieSlice_t*)[dataId elementAt:(I)])
  28. #define    matrixCELL(M,I)    (pFlags.useCellTags?[M findCellWithTag:I]:[[M cellList] objectAt:I])
  29. #define    ibSliceCOUNT    ((dftSliceCount>0)?dftSliceCount:3)
  30.  
  31. // -------------------------------------------------------------------------------------
  32. // percent display format
  33. static char    percentFormat[16] = { "%.1f%%" };
  34.  
  35. // -------------------------------------------------------------------------------------
  36. // pie slice data
  37. typedef struct pieSlice_s {
  38.   id            labelSrc;                // label source for slice
  39.   const char    *label;                    // slice label
  40.   id            valueSrc;                // value source for slice
  41.   float            value;                    // current slice value
  42.   float            percent;                // current slice percent
  43.   float            endAngle;                // ending angle
  44. } pieSlice_t;
  45.  
  46. // -------------------------------------------------------------------------------------
  47. @implementation PieChart
  48.  
  49. // -------------------------------------------------------------------------------------
  50. // archive version number
  51. static BOOL    _nibMode = NO;
  52. + initialize
  53. {
  54.   [self setVersion:VERSION];
  55.   if (!strcmp([NXApp appName], "InterfaceBuilder")) _nibMode = YES;
  56.   return self;
  57. }
  58.  
  59. // -------------------------------------------------------------------------------------
  60. // return inspector name
  61. - (const char*)getInspectorClassName { return "PieChartInspector"; }
  62.  
  63. // -------------------------------------------------------------------------------------
  64. // color/gray support 
  65.  
  66. /* convert color to gray */
  67. static float _colorGray(NXColor color)
  68. {
  69.   float    gray;
  70.   NXConvertColorToGray(color, &gray);
  71.   return gray;
  72. }
  73.  
  74. /* set color/gray */
  75. static void _setColor(NXColor color, BOOL isColor)
  76. {
  77.   if (isColor) NXSetColor(color); else PSsetgray(_colorGray(color));
  78. }
  79.  
  80. // -------------------------------------------------------------------------------------
  81. // color table support
  82.  
  83. /* return gray value for slice number */
  84. static float _grayValue(int i, int n)
  85. {
  86.   if ((++i) & 1) return (float)(i + 1) / (float)(2 * n);
  87.   return (((float)i / 2.0) + (float)((n + 1) / 2)) / (float)n;
  88. }
  89.  
  90. - _initColorTable:(int)count clear:(BOOL)clear
  91. {
  92.   int    i;
  93.  
  94.   /* clear existing table */
  95.   if (clear && sliceColors) { [sliceColors free]; sliceColors = (id)nil; }
  96.  
  97.   /* set default slice count */
  98.   dftSliceCount = (count >= 0)? count : 0;
  99.   if (dftSliceCount <= 0) return self;
  100.   
  101.   /* build table */
  102.   if (!sliceColors) {
  103.     sliceColors = [[[Storage alloc] initCount:1 elementSize:sizeof(NXColor)
  104.       description:(char*)nil] empty];
  105.   }
  106.   
  107.   /* fill table */
  108.   for (i = [sliceColors count]; i < dftSliceCount; i++) {
  109.     NXColor color = NXConvertGrayToColor(_grayValue(i, dftSliceCount));
  110.     [sliceColors addElement:&color];
  111.   }
  112.   
  113.   return self;
  114. }
  115.  
  116. // -------------------------------------------------------------------------------------
  117. // initialize data
  118.  
  119. /* empty dataId */
  120. - _initPieData
  121. {
  122.   int    i;
  123.  
  124.   /* create data holder if non-existant */
  125.   if (!dataId) {
  126.     dataId = [Storage newCount:1 elementSize:sizeof(pieSlice_t) description:(char*)nil];
  127.     [dataId empty];
  128.   }
  129.   
  130.   /* free any existing data */
  131.   if (i = [dataId count]) {
  132.     while (i) {
  133.       const char *lbl = pieSLICE(--i)->label;
  134.       if (lbl) free((char*)lbl);
  135.     }
  136.     [dataId empty];
  137.   }
  138.   
  139.   return dataId;
  140. }
  141.  
  142. /* fill dataId */
  143. - _fillPieData:(int)count
  144. {
  145.   int    i;
  146.   for (i = 0; i < count; i++) [self setValue:100.0 forSlice:i];
  147.   return dataId;
  148. }
  149.  
  150. /* IB use (_nibMode assumed): fill pie with default data */
  151. - _initDefaultPie:(int)sliceCount
  152. {
  153.   [self _initPieData];    // clear existing data
  154.   [self _fillPieData:((sliceCount>=0)?sliceCount:ibSliceCOUNT)];
  155.   return self;
  156. }
  157.  
  158. // -------------------------------------------------------------------------------------
  159. // initialize view
  160.  
  161. /* main init */
  162. - initFrame:(const NXRect*)r
  163. {
  164.   
  165.   /* init view */
  166.   [super initFrame:r];
  167.  
  168.   /* init flags */
  169.   memset(&pFlags, 0, sizeof(pFlags));
  170.   pFlags.showPercent = YES;
  171.   pFlags.drawOutline = YES;
  172.   pFlags.drawLabels  = YES;
  173.   pFlags.drawInColor = YES;
  174.   pFlags.acceptColor = NO;
  175.  
  176.   /* initialize with defaults */
  177.   outlineColor    = NXConvertGrayToColor(NX_BLACK);
  178.   labelColor      = NXConvertGrayToColor(NX_BLACK);
  179.   backgroundColor = NXConvertGrayToColor(NX_LTGRAY);
  180.   currentFont      = [Font newFont:"Helvetica" size:10.0 matrix:NX_IDENTITYMATRIX];
  181.   labelMatrix     = (id)nil;
  182.   valueMatrix     = (id)nil;
  183.   outputPercent      = (id)nil;
  184.   delegate          = self;
  185.   
  186.   /* init data holder */
  187.   dataId = (id)nil;
  188.   sliceColors = (id)nil;
  189.   [self setDefaultSliceCount:0];
  190.   [self _initPieData];
  191.   if (_nibMode) [self _fillPieData:ibSliceCOUNT]; 
  192.  
  193.   return self;
  194. }
  195.  
  196. /* free pie chart */
  197. - free
  198. {
  199.   [[self _initPieData] free];
  200.   [self setDefaultSliceCount:0];
  201.   return [super free];
  202. }
  203.  
  204. // -------------------------------------------------------------------------------------
  205. // adding data to the pie chart
  206.  
  207. /* return cell title */
  208. static const char *_cellTitle(id cellId)
  209. {
  210.   const char *label = (const char*)nil;
  211.   if (!cellId) return (const char*)nil;
  212.   if ([cellId respondsTo:@selector(title)])       label = [cellId title]; else
  213.   if ([cellId respondsTo:@selector(stringValue)]) label = [cellId stringValue];
  214.   return label;
  215. }
  216.  
  217. /* fill holes in data */
  218. static void _fillData(id dataId, int limit)
  219. {
  220.   int            i;
  221.   pieSlice_t    data = { (id)nil, (const char*)nil, (id)nil, 0.0, 0.0, 0.0 };
  222.   for (i = [dataId count]; i <= limit; i++) [dataId addElement:&data];
  223. }
  224.  
  225. /* set pie slice data */
  226. - _setPieSlice:(int)slice :(id)vs:(float)v:(BOOL)setV :(id)ls:(char*)l:(BOOL)setL
  227. {
  228.   pieSlice_t    *pieSlice;
  229.   
  230.   /* fill up any holes in data */
  231.   if (slice < 0) return (id)nil;
  232.   _fillData(dataId, slice);
  233.   pieSlice = pieSLICE(slice);
  234.   
  235.   /* set label */
  236.   if (setL) {
  237.     if (pieSlice->label) free((char*)pieSlice->label);
  238.     pieSlice->labelSrc = ls;
  239.     pieSlice->label = (!ls && l)? NXCopyStringBuffer(l) : (const char*)nil;
  240.   }
  241.   
  242.   /* set value */
  243.   if (setV) {
  244.     pieSlice->valueSrc = vs;
  245.     pieSlice->value = (!vs)? v : 0.0;
  246.   }
  247.  
  248.   return self;
  249. }
  250.  
  251. /* explicitly set a pie slice value (no re-display) */
  252. - setValue:(float)value forSlice:(int)slice
  253. {
  254.   return [self _setPieSlice:slice :(id)nil:value:YES :(id)nil:(char*)nil:NO];
  255. }
  256.  
  257. /* set pie slice value and label */
  258. - setValue:(float)value andLabel:(const char*)label forSlice:(int)slice
  259. {
  260.   return [self _setPieSlice:slice :(id)nil:value:YES :(id)nil:(char*)label:YES];
  261. }
  262.  
  263. /* redisplay */
  264. - update:sender
  265. {
  266.   return [self display];
  267. }
  268.  
  269. // -------------------------------------------------------------------------------------
  270. // set plot options
  271. // -------------------------------------------------------------------------------------
  272.  
  273. /* set background transparent */
  274. - setBackgroundTransparent:(BOOL)flag
  275. {
  276.   pFlags.transparent = flag?YES:NO;
  277.   return self;
  278. }
  279.  
  280. /* return background transparent state */
  281. - (BOOL)backgroundTransparent
  282. {
  283.   return pFlags.transparent;
  284. }
  285.  
  286. /* set font (make a new unflipped font copy) */
  287. // This is done this way on purpose
  288. - setFont:fontId
  289. {
  290.   currentFont = [Font newFont:[fontId name] size:[fontId pointSize] matrix:NX_IDENTITYMATRIX];  
  291.   return self;
  292. }
  293.  
  294. /* return current font (NX_FLIPPEDMATRIX) */
  295. // This is done this way on purpose (IB can't handle it properly any other way)
  296. - font
  297. {
  298.   return [Font newFont:[currentFont name] size:[currentFont pointSize]];
  299. }
  300.  
  301. /* set background gray */
  302. - setBackgroundGray:(float)aGray
  303. {
  304.   backgroundColor = NXConvertGrayToColor(aGray);
  305.   return self;
  306. }
  307.  
  308. /* return current background gray */
  309. - (float)backgroundGray
  310. {
  311.   float    gray;
  312.   NXConvertColorToGray(backgroundColor, &gray);
  313.   return gray;
  314. }
  315.  
  316. /* set background color */
  317. - setBackgroundColor:(NXColor)aColor
  318. {
  319.   backgroundColor = aColor;
  320.   return self;
  321. }
  322.  
  323. /* return current background color */
  324. - (NXColor)backgroundColor
  325. {
  326.   return backgroundColor;
  327. }
  328.  
  329. /* set outline gray */
  330. - setOutlineGray:(float)aGray
  331. {
  332.   outlineColor = NXConvertGrayToColor(aGray);
  333.   return self;
  334. }
  335.  
  336. /* return current outline gray */
  337. - (float)outlineGray
  338. {
  339.   float    gray;
  340.   NXConvertColorToGray(outlineColor, &gray);
  341.   return gray;
  342. }
  343.  
  344. /* set text label gray */
  345. - setLabelGray:(float)aGray
  346. {
  347.   labelColor = NXConvertGrayToColor(aGray);
  348.   return self;
  349. }
  350.  
  351. /* return current label gray */
  352. - (float)labelGray
  353. {
  354.   float    gray;
  355.   NXConvertColorToGray(labelColor, &gray);
  356.   return gray;
  357. }
  358.  
  359. /* set show percent on pie slice */
  360. - setShowPercent:(BOOL)flag
  361. {
  362.   pFlags.showPercent = flag?YES:NO;
  363.   return self;
  364. }
  365.  
  366. /* return show percent status */
  367. - (BOOL)showPercent
  368. {
  369.   return pFlags.showPercent;
  370. }
  371.  
  372. /* set pie slice outline */
  373. - setDrawSliceOutline:(BOOL)flag
  374. {
  375.   pFlags.drawOutline = flag?YES:NO;
  376.   return self;
  377. }
  378.  
  379. /* return pie slice outline status */
  380. - (BOOL)drawSliceOutline
  381. {
  382.   return pFlags.drawOutline;
  383. }
  384.  
  385. /* set draw label */
  386. - setDrawLabels:(BOOL)flag
  387. {
  388.   pFlags.drawLabels = flag?YES:NO;
  389.   return self;
  390. }
  391.  
  392. /* return draw label status */
  393. - (BOOL)drawLabels
  394. {
  395.   return pFlags.drawLabels;
  396. }
  397.  
  398. /* set specific slice color */
  399. - setSliceColor:(NXColor)color at:(int)slice
  400. {
  401.   if (slice < 0) return self;
  402.   if (slice + 1 > dftSliceCount) [self _initColorTable:slice + 1 clear:NO];
  403.   [sliceColors replaceElementAt:slice with:&color];
  404.   return self;
  405. }
  406.  
  407. /* return slice color */
  408. - (NXColor)sliceColorAt:(int)slice
  409. {
  410.   int    count;
  411.  
  412.   /* check color table */
  413.   if (sliceColors && (slice < [sliceColors count])) {
  414.     NXColor color = *((NXColor*)[sliceColors elementAt:slice]);
  415.     return color;
  416.   }
  417.  
  418.   /* return gray */
  419.   count = (dftSliceCount > 0)? dftSliceCount : (dataId? [dataId count] : 0);
  420.   if (slice >= count) return NXConvertGrayToColor(NX_WHITE);
  421.   return NXConvertGrayToColor(_grayValue(slice, count));
  422.   
  423. }
  424.  
  425. /* set default slice count */
  426. - setDefaultSliceCount:(int)count
  427. {
  428.   return [self _initColorTable:count clear:YES];
  429. }
  430.  
  431. /* return current default slice count */
  432. - (int)defaultSliceCount
  433. {
  434.   return dftSliceCount;
  435. }
  436.  
  437. /* allow accepting color changes */
  438. - setAcceptColor:(BOOL)flag
  439. {
  440.   pFlags.acceptColor = flag;
  441.   if (flag) pFlags.drawInColor = YES;
  442.   return self;
  443. }
  444.  
  445. /* set delegate */
  446. - setDelegate:aDelegate
  447. {
  448.   delegate = aDelegate;
  449.   return self;
  450. }
  451.  
  452. /* return delegate */
  453. - delegate
  454. {
  455.   return delegate;
  456. }
  457.  
  458. // -------------------------------------------------------------------------------------
  459. // outlets
  460. // -------------------------------------------------------------------------------------
  461.  
  462. /* load value and label matrix into pie */
  463. - _loadMatrix:sender
  464. {
  465.   int i;
  466.   id vList = valueMatrix? [valueMatrix cellList] : (id)nil;
  467.   id lList = labelMatrix? [labelMatrix cellList] : (id)nil;
  468.   [self _initPieData];    // clear existing data
  469.   if (!vList) return self;
  470.   for (i = 0; i < [vList count]; i++) {
  471.     id vCell = [vList objectAt:i];
  472.     id lCell = (lList && (i < [lList count]))? [lList objectAt:i] : (id)nil;
  473.     [self _setPieSlice:(pFlags.useCellTags?[vCell tag]:i)
  474.                       :vCell:0.0:YES :lCell:(char*)nil:YES];
  475.   }
  476.   [self display];
  477.   return self;
  478. }
  479.  
  480. /* set value matrix */
  481. - setValueMatrix:anObject
  482. {
  483.   if (![anObject isKindOf:[Matrix class]]) return self;
  484.   valueMatrix = anObject;
  485.   [self perform:@selector(_loadMatrix:) with:(id)nil afterDelay:1 cancelPrevious:YES];
  486.   return self;
  487. }
  488.  
  489. /* set label matrix */
  490. - setLabelMatrix:anObject
  491. {
  492.   if (![anObject isKindOf:[Matrix class]]) return self;
  493.   labelMatrix = anObject;
  494.   [self perform:@selector(_loadMatrix:) with:(id)nil afterDelay:1 cancelPrevious:YES];
  495.   return self;
  496. }
  497.  
  498. /* set form matrix */
  499. - setFormMatrix:anObject
  500. {
  501.   if (![anObject isKindOf:[Matrix class]]) return self;
  502.   labelMatrix = valueMatrix = anObject;
  503.   [self perform:@selector(_loadMatrix:) with:(id)nil afterDelay:1 cancelPrevious:YES];
  504.   return self;
  505. }
  506.  
  507. /* set output percent matrix */
  508. - setPercentMatrix:anObject
  509. {
  510.   if (![anObject isKindOf:[Matrix class]]) return self;
  511.   outputPercent = anObject;
  512.   return self;
  513. }
  514.  
  515. // -------------------------------------------------------------------------------------
  516. // plot data in pie chart
  517. // -------------------------------------------------------------------------------------
  518.  
  519. /* pie chart plot */
  520. - _drawPie
  521. {
  522.   int        i, dataCnt = [dataId count];
  523.   float        sum, curAngle, angle;
  524.   
  525.   /* set fonts and gray */
  526.   [currentFont set];
  527.   PSsetlinewidth(1.0);
  528.  
  529.   /* determine center and radius */
  530.   pieCenter.x = bounds.W / 2.0;
  531.   pieCenter.y = bounds.H / 2.0;
  532.   if (!pFlags.drawLabels) pieRadius = (MIN(bounds.W, bounds.H) - 2.0) / 2.0;
  533.   else pieRadius = (MIN(bounds.W, bounds.H) - (4.5 * [currentFont pointSize])) / 2.0;
  534.   if (pieRadius < 1.0) pieRadius = 1.0;
  535.  
  536.   /* update values and sum */
  537.   for (sum = 0.0, i = 0; i < dataCnt; i++) {
  538.     pieSlice_t *slice = pieSLICE(i);
  539.     if (slice->valueSrc) slice->value = [slice->valueSrc floatValue];
  540.     sum += slice->value;
  541.   }
  542.   
  543.   /* draw pie ('slice->value' is current) */
  544.   if (sum) {
  545.     char    *lp, label[256], pct[16];
  546.     float    oGray, lGray;
  547.   
  548.     /* draw slices */
  549.     for (curAngle = 0.0, i = 0; i < dataCnt; i++) {
  550.       pieSlice_t *slice = pieSLICE(i);
  551.       slice->percent = slice->value * 100.0 / sum;
  552.       angle = slice->percent * 3.60;
  553.       slice->endAngle = curAngle + angle;
  554.       if (!angle) continue;
  555.       if (lp = (char*)_cellTitle(slice->labelSrc)) strcpy(label, lp); else
  556.       if (slice->label) strcpy(label, slice->label);
  557.       else sprintf(label, "#%d", i + 1);
  558.       if (pFlags.showPercent) {
  559.         sprintf(pct, percentFormat, slice->percent);
  560.         sprintf(&label[strlen(label)], " (%s)", pct);
  561.       }
  562.       _setColor([self sliceColorAt:i], pFlags.drawInColor);
  563.       NXConvertColorToGray(outlineColor, &oGray);
  564.       NXConvertColorToGray(labelColor, &lGray);
  565.       _pieDrawSlice(label, [currentFont pointSize], pieCenter.x, pieCenter.y, pieRadius,
  566.               curAngle, slice->endAngle, (pFlags.drawOutline?oGray:-1.0),
  567.             (pFlags.drawLabels?lGray:-1.0));
  568.       curAngle += angle;
  569.     }
  570.   
  571.     /* update output percent matrix */
  572.     if (outputPercent) {
  573.       for (i = 0; i < dataCnt; i++) {
  574.         id cellId = matrixCELL(outputPercent, i);
  575.         if (cellId) {
  576.           pieSlice_t *slice = pieSLICE(i);
  577.           sprintf(pct, percentFormat, slice->percent);
  578.           [cellId setStringValue:pct];
  579.         }
  580.       }
  581.     }
  582.     
  583.   }
  584.  
  585.   return self;
  586.   
  587. }
  588.   
  589. /* draw pie view */
  590. - drawSelf:(const NXRect *)r :(int)n
  591. {
  592.   if (!pFlags.transparent) {
  593.     _setColor(backgroundColor, pFlags.drawInColor);
  594.     NXRectFill(&bounds);
  595.   }
  596.   [self _drawPie];
  597. //if (isFirstResponder) NXHighlightRect(&bounds);
  598.   return self;
  599. }
  600.   
  601. // -------------------------------------------------------------------------------------
  602. // object archiving
  603.  
  604. - read:(NXTypedStream*)s
  605. {
  606.   int    ver, i, cnt;
  607.   sliceColors = (id)nil;
  608.   [super read:s];
  609.   NXReadTypes(s, "i",&ver);
  610.   NXReadTypes(s, "s@",&pFlags,¤tFont);
  611.   backgroundColor = NXReadColor(s);
  612.   outlineColor    = NXReadColor(s);
  613.   labelColor      = NXReadColor(s);
  614.   NXReadTypes(s, "i",&cnt);
  615.   [self setDefaultSliceCount:cnt];    // set dftSliceCount
  616.   for (i = 0; i < dftSliceCount; i++) [self setSliceColor:NXReadColor(s) at:i];
  617.   delegate         = self;
  618.   labelMatrix    = (id)nil;
  619.   valueMatrix    = (id)nil;
  620.   outputPercent     = (id)nil;
  621.   dataId         = (id)nil;
  622.   pFlags.drawInColor = YES;
  623.   [self _initPieData];
  624.   if (_nibMode) [self _fillPieData:ibSliceCOUNT];
  625.   return self;
  626. }
  627.  
  628. - write:(NXTypedStream*)s
  629. {
  630.   int    ver = [[self class] version];
  631.   int    i;
  632.   [super write:s];
  633.   NXWriteTypes(s, "i",&ver);
  634.   NXWriteTypes(s, "s@",&pFlags,¤tFont);
  635.   NXWriteColor(s, backgroundColor);
  636.   NXWriteColor(s, outlineColor);
  637.   NXWriteColor(s, labelColor);
  638.   NXWriteTypes(s, "i",&dftSliceCount);
  639.   for (i = 0; i < dftSliceCount; i++) NXWriteColor(s, [self sliceColorAt:i]);
  640.   return self;
  641. }
  642.  
  643. // -------------------------------------------------------------------------------------
  644. // firstResponder for getting copy/paste commands
  645.  
  646. /* allow becoming first responder */
  647. - (BOOL)acceptsFirstResponder { return YES; }
  648.  
  649. /* become first responder */
  650. - becomeFirstResponder
  651. {
  652.   isFirstResponder = YES;
  653.   [self display];
  654.   return self;
  655. }
  656.  
  657. /* resign first responder */
  658. - resignFirstResponder
  659. {
  660.   isFirstResponder = NO;
  661.   [self display];
  662.   return self;
  663. }
  664.  
  665. // -------------------------------------------------------------------------------------
  666. // mouseDown tracking
  667.  
  668. /* handle pie slice selected */
  669. - selectedPieSlice:(int)slice
  670. {
  671.   id    cellId, matrix = actionMatrix;
  672.   if (!matrix && valueMatrix && ([valueMatrix target] != self)) matrix = valueMatrix;
  673.   if (matrix && (slice >= 0) && (cellId = matrixCELL(matrix, slice))) {
  674.     [matrix selectCell:cellId];
  675.     [matrix sendAction];
  676.   }
  677.   return self;
  678. }
  679.  
  680. /* slice hit detection */
  681. - (int)_sliceHitDetection:(const NXPoint*)mousePoint
  682. {
  683.   int        index, dataCnt = dataId? [dataId count] : 0;
  684.   float        len, xLen, yLen, angle;
  685.   NXPoint    pt = *mousePoint;
  686.   
  687.   /* check for valid pie */
  688.   if (!dataCnt) return -1;
  689.   [self convertPoint:&pt fromView:(id)nil];
  690.   
  691.   /* determine distance from center */
  692.   xLen = pt.x - pieCenter.x;
  693.   yLen = pt.y - pieCenter.y;
  694.   len = (float)hypot((double)xLen, (double)yLen);
  695.   if (len > pieRadius) return -1;
  696.   
  697.   /* determine angle */
  698.   angle = (float)(atan2(yLen, xLen) * 180.0 / M_PI);
  699.   if (angle < 0.0) angle += 360.0;
  700.   
  701.   /* find pie slice */
  702.   for (index = 0; index < dataCnt; index++) {
  703.     pieSlice_t *slice = pieSLICE(index);
  704.     if (angle <= slice->endAngle) return index;
  705.   }
  706.   
  707.   return -1;
  708. }
  709.  
  710. /* intercept mouse down */
  711. - mouseDown:(NXEvent*)e
  712. {
  713.   int    index;
  714.  
  715.   /* IB check */
  716.   if (_nibMode && (delegate != self))
  717.     return [delegate selectedPieSlice:[self _sliceHitDetection:&(e->location)]]; 
  718.   
  719.   /* check for slice selection */
  720.   if (e->data.mouse.click == 2) {
  721.     index = [self _sliceHitDetection:&(e->location)];
  722.     if ((index >= 0) && [delegate respondsTo:@selector(selectedPieSlice:)])
  723.       [delegate selectedPieSlice:index];
  724.   }
  725.   
  726.   return [super mouseDown:e];
  727. }
  728.  
  729. // -------------------------------------------------------------------------------------
  730. // IB use: color modification
  731.  
  732. /* accept color */
  733. - acceptColor:(NXColor)color atPoint:(const NXPoint*)point
  734. {
  735.   int    slice;
  736.   if (!pFlags.acceptColor) return self;
  737.   slice = [self _sliceHitDetection:point];
  738.   if (slice < 0) [self setBackgroundColor:color];
  739.   else if (dftSliceCount > 0) [self setSliceColor:color at:slice];
  740.   return [self display];
  741. }
  742.  
  743. @end
  744.